#region "Copyright (C) Lenny Granovsky. 2005-2013"
//This program is free software distribute under the terms of the GNU General Public License as published by
//the Free Software Foundation. Please read AssemblyInfo.cs file for more information.
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Web;
using System.Web.UI;
using System.Security.Permissions;

[assembly: WebResource("Web.Enhancements.Rest.RestClientJSDebug.js", "application/x-javascript")]
[assembly: WebResource("Web.Enhancements.Rest.RestClientJSRelease.js", "application/x-javascript")]
namespace Web.Enhancements.Rest
{
    /// <summary>
    /// Class creates a client automated javascript.
    /// </summary>
    [Serializable()]
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    internal class WebRestExplorer : IWebRestService
    {
        private WebRestClassConfiguration TypeConfig;
        private static object lockObject;

        static WebRestExplorer()
        {
            lockObject = "";
        }

        public WebRestExplorer()
        {
        }

        /// <summary>
        /// Renders javascript include from request like: "base.WebRestExplorer.rest?RenderClass&amp;typeName=TestWeb25.InfoPreviewHandler, TestWeb25, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" in BASE64 FORMAT.
        /// </summary>
        /// <param name="typeName">Fully qualified type name, including namespace and assembly, in BASE64 format.</param>
        /// <returns>Returns javascript code string.</returns>
        [WebRestMethodAttribute(false, ContentType = "application/x-javascript")]
        [WebRestCache(Duration = 86400, Location = OutputCacheLocation.Any, VaryByParam = "All", Enabled = true)] //24 hours caching
        public string RenderClass(string typeName)
        {
            typeName = System.Text.Encoding.Default.GetString(Convert.FromBase64String(typeName));
            string assemblyName = "";
            string classTypeName = TypeMapper.ExtractFullTypeName(typeName, out assemblyName);
            string key = InfoCaching.JSClassCache.CreateKey(classTypeName);
            //try from cache first
            if (InfoCaching.JSClassCache.Current.ContainsKey(key))
                return InfoCaching.JSClassCache.Current[key];

            //otherwise - create new
            object target = null;
            try
            {
                if (assemblyName.Length == 0)
                    target = Activator.CreateInstance(Type.GetType(classTypeName, true, true));
                else
                {
                    target = Activator.CreateInstance(assemblyName, classTypeName);
                    target = ((System.Runtime.Remoting.ObjectHandle)target).Unwrap();
                }
            }
            catch (Exception e)
            {
                throw new WebRestException(ErrorMessages.ResourceManager.GetString("WebRestExplorerCannotCreateTypeInstance"), typeName, e);
            }
            Type targetType = target.GetType();
            if (targetType.GetInterface("IWebRestService") == null)
                throw new WebRestException(ErrorMessages.ResourceManager.GetString("InvalidClassTypeNoWebRestServiceAttribute"), typeName);
            string tmp = this.BuildClassJSClient(targetType);
            if (!InfoCaching.JSClassCache.Current.ContainsKey(key))
            {   //handle multithreading before adding
                lock (lockObject)
                {
                    if (!InfoCaching.JSClassCache.Current.ContainsKey(key))
                        InfoCaching.JSClassCache.Current.Add(key, tmp);
                }
            }
            return tmp;
        }

        public string BuildClassJSClient(Type type)
        {
            MethodInfo[] mis = type.GetMethods();
            System.Text.StringBuilder sbr = new StringBuilder();
            this.TypeConfig = WebRestSettings.Current.TypeMappings.Find(type.FullName, type.Assembly.FullName);
            if (this.TypeConfig == null)
                this.TypeConfig = new WebRestClassConfiguration(type.FullName, type.Assembly.FullName);

            if (this.TypeConfig.IncludeNamespaceOnClientSide)
            {
                sbr.AppendFormat("webRestExplorer.registerNS('{0}');\n", this.TypeConfig.AliasNamespace);
                sbr.AppendFormat("{0}.{1} = function {1}() {{}};\n", this.TypeConfig.AliasNamespace, type.Name);
            }
            else
                sbr.AppendFormat("function {0}(){{}}\n", this.TypeConfig.AliasName);
            sbr.AppendFormat(" {0}.version = '{1}';\n", this.TypeConfig.AliasName, type.Assembly.GetName().Version.ToString());
            object[] tmp = null;
            foreach (MethodInfo mi in mis)
            {
                tmp = mi.GetCustomAttributes(typeof(WebRestMethodAttribute), false);
                if (tmp.Length > 0)
                    sbr.AppendFormat("{0}\n", BuildMethodJSClient(type, mi, (tmp[0] as WebRestMethodAttribute).HandleClientMousePosition));
            }
            return sbr.ToString();
        }

        private string BuildMethodJSClient(Type type, MethodInfo mInfo, bool handleClientMousePosition)
        {
            string callbackFunction = WebRestClient.GetJSClientDefaultCallbackName(type, mInfo.Name);
            ParameterInfo[] pis = mInfo.GetParameters();
            System.Text.StringBuilder sbr = new StringBuilder();
            //build method itself
            sbr.AppendFormat(" {0}.{1} = function {1}(", this.TypeConfig.AliasName, mInfo.Name);
            foreach (ParameterInfo pi in pis)
                sbr.AppendFormat("{0},", pi.Name);
            sbr.Append("dynamicCallback) {\n");
            int maxParamsCount = pis.Length + 1;
            sbr.AppendFormat("  var extraParams = (arguments.length <= {0}) ? null : Array.prototype.slice.call(arguments, {0});\n", maxParamsCount.ToString());
            sbr.AppendFormat("  var callbackMethod = (dynamicCallback!=null && typeof(dynamicCallback)!='undefined') ? dynamicCallback : ((typeof({0})!='function') ? webRestExplorer.defaultCallback : {0});\n", callbackFunction);
            if (handleClientMousePosition)
                sbr.Append("  var oEvent=window.Event;if(oEvent==null){oEvent=window.event;if(oEvent==null){if(arguments.callee.caller!=null)oEvent=arguments.callee.caller.arguments[0];}}if (oEvent!=null){lastEvent=oEvent;webRestExplorer.calculateCurrentPosition(oEvent);}\n");
            sbr.AppendFormat("  webRestExplorer.getDataFromServer(\"{0}", WebRestClient.GetUrlFunctionCall2(this.TypeConfig.AliasName, mInfo.Name, "", this.TypeConfig.ReplaceDotWithPathSeparator));
            //add parameters
            foreach (ParameterInfo pi in pis)
                sbr.AppendFormat("&{0}=\" + encodeURIComponent({0}) + \"", pi.Name);
            sbr.Append("\", callbackMethod, extraParams);");
            sbr.Append("\n };\n");
            return sbr.ToString();
        }
    }
}
